#include "edit_song_feature.h"

#include "../../model/core_utils.h"
#include "../../model/core_io.h"

#include <QString>
#include <QPainterPath>
#include <QTime>
#include <QList>

// constructor and destructor
EditSongFeature::EditSongFeature(int _id, Engine *_engine, int _x, int _y, int _w, int _h,
                                 ControllerProxy *_cProxy, MainWidget *_mw)
                                     :Feature(_id, _engine, _mw, _cProxy, _x, _y, _w, _h),
                                     showMenuAnime(_engine->fps, 400),
                                     editMenuShowAnime(_engine->fps, 800),
                                     om_ni(new Linear_NI<float>(), true)
{
    generalInfoLabel = 0;
    psEditLabel = 0;
    loopEditLabel = 0;

    // general info widget
    for( int i=0; i<7; i++ )
    {
        infoFixedLabel[ i ] = 0;
        infoLabel[ i ] = 0;
    }

    saveLabel = 0;
    editPsLabel = 0;
    editLoopLabel = 0;
    pitchSlider = 0;

    // ps editor widgets
    psList = 0;

    for( int i=0; i<6; i++ )
    {
        psFixedLabel[ i ] = 0;
    }

    psNameBox = 0;
    psStartLabel = 0;
    psEndLabel = 0;
    psLengthLabel = 0;
    psSpeedLabel = 0;

    psSpeedSlider = 0;
    psPitchLabel = 0;
    psPitchSlider = 0;
    psTitleLabel = 0;
    psPlayerSlider = 0;
    psTimeLabel = 0;
    psAddButton = 0;
    psRemoveButton = 0;
    psInsertButton = 0;
    psClearButton = 0;
    psSetStartButton = 0;
    psSetEndButton = 0;
    psControls = 0;
    psInPlayFixedLabel = 0;
    psInPlayLabel = 0;
    ignoreTextChange = false;

    song = 0;

    BuildWidgets();
    BuildAnimations();

    showingGeneralInfo = false;
    showingPsEditor = false;
    showingLoopEditor = false;
    reshowingMenu = false;
    hiding = false;
    switchingToPsEditMode = false;

    switchingToLoopMode = false;
}

EditSongFeature::~EditSongFeature(){}

// paint
void EditSongFeature::Paint(QPainter &painter)
{
    painter.setPen(QColor(0, 0, 255, 255));

    /* --------------------- menu mode --------------------------------------------------------- */
    if(generalInfoX > 1)
    {
        UnderlineText(painter, generalInfoLabel, &generalInfoX);
    }

    if(psEditLabelX > 1)
    {
        UnderlineText(painter, psEditLabel, &psEditLabelX);
    }

    if(loopEditLabelX > 1)
    {
        UnderlineText(painter, loopEditLabel, &loopEditLabelX);
    }

    if(headerLineLength > 1)
    {
        painter.drawLine(x + 300, y + 20, x + 300 + headerLineLength, y + 20);
    }
    /* ---------------------------------------------------------------------------------------- */

    if( currentMode == PSEditMode || currentMode == LoopEditMode)
    {
        int msecs = Player::GetInstance()->GetTime();

        // updates the slider position
        if( playing && updateSlider )
        {
            float nPos = ( float )msecs / song->length;
            psPlayerSlider->SetPosition( nPos );
        }

        int sel = psList->GetSelectedIndex();
        if( currentMode == PSEditMode )
        {
            int curSeg = song->GetSegmentIndexAtTime( msecs );

            if( sel != -1 && curSeg != -1 )
            {
                psInPlayLabel->SetText( song->segments[ curSeg ].name );

                PracticeSegment activeSegment = song->segments.at( sel );
                if( msecs > activeSegment.end || sel == 0)
                {
                    psSetStartButton->SetX( -1000 );
                }
                else
                {
                    psSetStartButton->SetX( psSpeedSlider->GetX() );
                }

                if( msecs < activeSegment.start || sel == song->segments.size() - 1 )
                {
                    psSetEndButton->SetX( -1000 );
                }
                else
                {
                    psSetEndButton->SetX( psSpeedSlider->GetX() );
                }
            }
        }
        else    // loop edit mode
        {
            if( sel != -1 )
            {
                Loop* l = &( song->loops[ sel ] );

                if( msecs > l->end )
                {
                    psSetStartButton->SetX( -1000 );
                }
                else
                {
                    psSetStartButton->SetX( psNameBox->GetX() + 120 );
                }

                if( msecs < l->start )
                {
                    psSetEndButton->SetX( -1000 );
                }
                else
                {
                    psSetEndButton->SetX( psNameBox->GetX() + 120 );
                }

                if( psList->GetX() >= 0 )
                {
                    painter.drawPixmap( lpActiveRect,
                                        l->active? *onPixmap : *offPixmap,
                                        onPixmap->rect() );
                    QString activeString = l->active? "Yes" : "No";
                    lpActiveLabel->SetText( activeString );
                }
            }
            else
            {
                lpActiveLabel->SetText( "" );
            }
        }

        if( psTimeLabel->GetX() > 0 )
        {
            painter.setPen( Qt::black );
            painter.setBrush( QBrush( QColor( 0, 0, 0, 0 ), Qt::NoBrush ) );
            painter.drawRect( x + psTimeLabel->GetX() + psTimeLabel->GetWidth()
                              - psTimeLabel->GetTextWidth() - 10, y + psTimeLabel->GetY(),
                              psTimeLabel->GetTextWidth() + 10, psTimeLabel->GetHeight() );
        }
    }
}

// visibility handling
void EditSongFeature::doShow()
{
    song = Player::GetInstance()->song;

    currentMode = MenuMode;
    showMenuAnime.Stop();
    showMenuAnime.Play();
    visible = true;
    paintEnabled = true;
}

void EditSongFeature::doHide()
{
    visible = false;
    hiding = true;

    switch(currentMode)
    {
    case MenuMode:
        showMenuAnime.Reverse(true);
        showMenuAnime.Play();
        break;
    case GeneralInfo:
        UnloadGeneralInfo();
        break;
    case PSEditMode:
        UnloadPSEditor();
        break;
    case LoopEditMode:
        UnloadLoopEditor();
        break;
    }
}

void EditSongFeature::OnShow(Widget* w)
{
    if(reshowingMenu)
    {
        eventCount--;
        if(eventCount == 0)
        {
            reshowingMenu = false;
            animeGraph.Get(generalInfoX_id)->Enable();
            animeGraph.Get(psEditLabelX_id)->Enable();
            animeGraph.Get(loopEditLabelX_id)->Enable();
            animeGraph.Get(generalInfoY_id)->Enable();
            animeGraph.Get(psEditLabelY_id)->Enable();
            animeGraph.Get(loopEditLabelY_id)->Enable();
            animeGraph.Get(generalInfoTx_id)->Enable();
            animeGraph.Get(psEditLabelTx_id)->Enable();
            animeGraph.Get(loopEditLabelTx_id)->Enable();
            currentMode = MenuMode;

            // post-actions
            if( hiding )
            {
                eventCount = 3;
                generalInfoLabel->Hide();
                psEditLabel->Hide();
                loopEditLabel->Hide();
            } else if( switchingToPsEditMode )
            {
                switchingToPsEditMode = false;
                OnClick( psEditLabel );
            } else if( switchingToLoopMode )
            {
                switchingToLoopMode = false;
                OnClick( loopEditLabel );
            }
        }
    }else
    {
        eventCount--;
        if(eventCount == 0)
        {
            TriggerEvent(0);
        }
    }
};

void EditSongFeature::OnHide(Widget* w)
{
    if(showingGeneralInfo || showingPsEditor || showingLoopEditor)
    {
        eventCount--;
        if(eventCount == 0)
        {
            if(showingGeneralInfo)
            {
                animeGraph.Get(generalInfoX_id)->Disable();
            }else if(showingPsEditor)
            {
                animeGraph.Get(psEditLabelX_id)->Disable();
            }else if(showingLoopEditor)
            {
                animeGraph.Get(loopEditLabelX_id)->Disable();
            }
            showMenuAnime.Reverse(true);
            showMenuAnime.Play();
        }
    }else
    {
        eventCount--;
        if(eventCount == 0)
        {
            if(hiding)
            {
                showMenuAnime.Reverse(true);
                showMenuAnime.Play();
            }else
            {
                paintEnabled = false;
                TriggerEvent(1);
            }
        }
    }
};

void EditSongFeature::OnMouseClick( QMouseEvent *e, int x_rel, int y_rel )
{
    if( currentMode == LoopEditMode )
    {
        int sel = psList->GetSelectedIndex();
        if( sel != -1 )
        {
            if( lpActiveRect.contains( x + x_rel, y + y_rel ) )
            {
                Loop* l = &( song->loops[ sel ] );
                l->active = !l->active;
            }
        }
    }
}

// ClickableLabelListener implementation
void EditSongFeature::OnClick(ClickableLabel* sender)
{
    if(sender == generalInfoLabel)          // menu click handling
    {
        if(currentMode == GeneralInfo)
        {
            UnloadGeneralInfo();
            return;
        }
        showingGeneralInfo = true;

        animeGraph.Get(psEditLabelY_id)->Disable();
        animeGraph.Get(loopEditLabelY_id)->Disable();
        animeGraph.Get(psEditLabelTx_id)->Disable();
        animeGraph.Get(loopEditLabelTx_id)->Disable();

        eventCount = 2;
        psEditLabel->Hide();
        loopEditLabel->Hide();
    }else if(sender == psEditLabel)
    {
        if(currentMode == PSEditMode)
        {
            UnloadPSEditor();
            return;
        }
        showingPsEditor = true;

        animeGraph.Get(generalInfoY_id)->Disable();
        animeGraph.Get(loopEditLabelY_id)->Disable();

        eventCount = 2;
        generalInfoLabel->Hide();
        loopEditLabel->Hide();
    }else if(sender == loopEditLabel)
    {
        if(currentMode == LoopEditMode)
        {
            UnloadLoopEditor();
            return;
        }
        showingLoopEditor = true;

        animeGraph.Get(generalInfoY_id)->Disable();
        animeGraph.Get(psEditLabelY_id)->Disable();

        eventCount = 2;
        generalInfoLabel->Hide();
        psEditLabel->Hide();
    }else if(sender == saveLabel)   // general info handling
    {
        cProxy->saveSong = true;
        cProxy->prevFeatureId = ControllerProxy::EDIT_SONG_FEATURE;
        cProxy->UnloadRequest(ControllerProxy::SAVE_FILE_DIALOG_FEATURE);
    } else if ( sender == editPsLabel )
    {
        switchingToPsEditMode = true;
        UnloadGeneralInfo();
    } else if ( sender == editLoopLabel )
    {
        switchingToLoopMode = true;
        UnloadGeneralInfo();
    }
}

// AnimationObserver implementation
void EditSongFeature::OnPause(Animation* a){}
void EditSongFeature::OnStop(Animation* a){}

void EditSongFeature::OnEnd(Animation* a)
{
    if(a == &showMenuAnime)
    {
        if(currentMode == MenuMode)
        {
            if(showingGeneralInfo || showingPsEditor || showingLoopEditor)
            {
                editMenuShowAnime.Stop();
                editMenuShowAnime.Play();
            }else
            {
                if(!a->IsReversed())
                {
                    Showed();

                    // post-actions
                    if( cProxy->postOpenAction != -1 )
                    {
                        int action = cProxy->postOpenAction;
                        cProxy->postOpenAction = -1;    // reset the post-open actions

                        switch( action )
                        {
                        case 0:     // show general info
                            OnClick( generalInfoLabel );
                            break;
                        case 1:     // show practice segment editor
                            OnClick( psEditLabel );
                            break;
                        case 2:     // show loops editor
                            OnClick( loopEditLabel );
                            break;
                        }
                    }

                }else
                {
                    hiding = false;
                    Hidden();
                }
            }
        }else
        {
            if(!a->IsReversed())
            {   // switching back to menu
                reshowingMenu = true;
                eventCount = 2;
                switch(currentMode)
                {
                case GeneralInfo:
                    psEditLabel->Show();
                    loopEditLabel->Show();
                    break;
                case PSEditMode:
                    generalInfoLabel->Show();
                    loopEditLabel->Show();
                    break;
                case LoopEditMode:
                    psEditLabel->Show();
                    generalInfoLabel->Show();
                    break;
                case MenuMode:
                    break;
                }
            }
        }
    }else if(a == &editMenuShowAnime)
    {
        if(a->IsReversed())
        {   // returning to menu
            showMenuAnime.Reverse(false);   // we draw the lines
            showMenuAnime.Play();
        }else
        {
            if(showingGeneralInfo)
            {
                LoadGeneralInfo();
            }else if(showingPsEditor)
            {
                LoadPSEditor();
            } else if(showingLoopEditor)
            {
                LoadLoopEditor();
            }
        }
    }
}

void EditSongFeature::OnPlay(Animation* a){}

// SliderListener implementation
void EditSongFeature::OnPosChanged( Slider* sender, double newPos )
{
    if( sender == pitchSlider )
    {
        int semitone = newPos * 24 - 12;
        infoLabel[6]->SetText( CoreUtils::GetPitchString( semitone ) );
        song->defaultPitch = semitone;
    } else if( sender == psSpeedSlider )
    {
        float newSpeed = newPos * 3;
        psSpeedLabel->SetText( CoreUtils::GetSpeedPerc( newSpeed ) );
    } else if( sender == psPitchSlider )
    {
        psPitchLabel->SetText( CoreUtils::GetPitchString( newPos * 24 - 12 ) );
    } else if( sender == psPlayerSlider )
    {
        // computes msecs offset
        int msecs = newPos * song->length;
        psTimeLabel->SetText( CoreUtils::GetTimePosition( msecs ) );
    }
}

void EditSongFeature::OnClickOrOnEndDrag( Slider* sender, double newPos )
{
    if( sender == psPlayerSlider )
    {
        int msec = newPos * song->length;

        if( currentMode == PSEditMode )
        {
            int segmentIndex = song->GetSegmentIndexAtTime( msec );
            SynchronizeWidgetsWithSong( segmentIndex );
        }

        Player::GetInstance()->SetTime( msec );
        updateSlider = true;
    }
    else if( sender == psSpeedSlider )
    {
        int sel = psList->GetSelectedIndex();
        song->segments[ sel ].tempo = newPos * 3;
        Player::GetInstance()->Synchronize();
    }
    else if( sender == psPitchSlider )
    {
        int sel = psList->GetSelectedIndex();
        song->segments[ sel ].pitch = newPos * 24 - 12;
        Player::GetInstance()->Synchronize();
    }
}

void EditSongFeature::OnStartDrag( Slider* sender )
{
    if( sender == psPlayerSlider )
    {
        updateSlider = false;
    }
}

// ListListener implementation
void EditSongFeature::OnSelect( List* sender, int index )
{
    ignoreTextChange = true;
    if( currentMode == PSEditMode && index != -1)
    {
        // populates the ps editor info labels and text boxe
        PracticeSegment ps = song->segments.at( index );
        psNameBox->SetText( ps.name );
        psStartLabel->SetText( CoreUtils::GetTimePosition( ps.start ) );
        psEndLabel->SetText( CoreUtils::GetTimePosition( ps.end ) );
        psLengthLabel->SetText( CoreUtils::GetTimePosition( ps.end - ps.start ) );
        psSpeedLabel->SetText( CoreUtils::GetSpeedPerc( ps.tempo ) );
        psSpeedSlider->SetPosition( ps.tempo/3 );
        psPitchLabel->SetText( CoreUtils::GetPitchString( ps.pitch ) );
        psPitchSlider->SetPosition( ( ps.pitch + 12 ) / 24.0 );
        psInPlayLabel->SetText( ps.name );

        float startPos = ps.start / (float)song->length;
        float endPos = ps.end / (float)song->length;
        psPlayerSlider->Highlight( startPos, endPos );

        if( index == 0 )
        {
            psSetStartButton->SetX( -1000 );
        } else
        {
            psSetStartButton->SetX( psSpeedSlider->GetX() );
        }

        if( index == song->segments.size() - 1 )
        {
            psSetEndButton->SetX( -1000 );
        } else
        {
            psSetEndButton->SetX( psSpeedSlider->GetX() );
        }
    }
    else if( currentMode == LoopEditMode )
    {
        Loop l;
        if( index != -1)
        {
            l = song->loops.at( index );
        }
        else
        {
            l.name = "";
            l.start = 0;
            l.end = 0;
            l.iterations = -1;
            l.active = false;
        }
        psNameBox->SetText( l.name );
        psStartLabel->SetText( CoreUtils::GetTimePosition( l.start ) );
        psEndLabel->SetText( CoreUtils::GetTimePosition( l.end ) );
        psLengthLabel->SetText( CoreUtils::GetTimePosition( l.end - l.start ) );
        float startPos = l.start / (float)song->length;
        float endPos = l.end / (float)song->length;
        psPlayerSlider->Highlight( startPos, endPos );
    }
}

void EditSongFeature::OnDoubleClick( List* sender, int index )
{
    int sel = psList->GetSelectedIndex();
    if( sel != -1 )
    {
        if( currentMode == PSEditMode )
        {
            int selStart = song->segments[ sel ].start;
            Player::GetInstance()->SetTime( selStart );
            SynchronizeWidgetsWithSong( sel );
        }
        else    // loop edit mode
        {
            int selStart = song->loops[ sel ].start;
            Player::GetInstance()->SetTime( selStart );
            SynchronizeWidgetsWithSong( sel );
        }

        //psList->SetItemSelected( sel );
    }
}

// PlayerControlsListener implementation
void EditSongFeature::OnControlClick( PlayerControls* sender, int controlId )
{
    if( controlId == PlayerControls::CTRL_PREV_SEGMENT && currentMode == PSEditMode )
    {
        int sel = psList->GetSelectedIndex();
        if ( sel > 0 )
        {
            int prevStart = song->segments[ sel - 1 ].start;
            Player::GetInstance()->SetTime( prevStart );
            SynchronizeWidgetsWithSong( sel - 1 );
        }
    }
    else if( controlId == PlayerControls::CTRL_PLAY )
    {
        playing = true;
        Player::GetInstance()->Play();
        Player::GetInstance()->SetVolume( psControls->volPos );
    }
    else if( controlId == PlayerControls::CTRL_STOP )
    {
        if ( Player::GetInstance()->Stop() )
        {
            psPlayerSlider->SetPosition( 0.0 );
        }
    }
    else if( controlId == PlayerControls::CTRL_PAUSE )
    {
        playing = false;
        Player::GetInstance()->Pause();
    }
    else if( controlId == PlayerControls::CTRL_NEXT_SEGMENT && currentMode == PSEditMode )
    {
        int sel = psList->GetSelectedIndex();
        if( sel < song->segments.size() - 1 )
        {
            int nextStart = song->segments[ sel + 1 ].start;
            Player::GetInstance()->SetTime( nextStart );
            SynchronizeWidgetsWithSong( sel + 1 );
        }
    }
}

void EditSongFeature::OnVolumeChange( PlayerControls* sender, float vol )
{
    Player::GetInstance()->SetVolume( vol );
}

// ButtonListener implementation
void EditSongFeature::OnClick(Button* b, QMouseEvent* e, int rel_x, int rel_y)
{
    if( b == psAddButton )
    {
        if( currentMode == PSEditMode )
        {
            if( song->AddSegment() )
            {
                SynchronizeWidgetsWithSong();
                Player::GetInstance()->Synchronize();
            }
        }
        else    // loop edit mode
        {
            song->AddLoop();
            SynchronizeWidgetsWithSong( song->loops.size() - 1 );
        }
    } else if( b == psRemoveButton )
    {
        if( currentMode == PSEditMode )
        {
            if( song->segments.size() == 1 ) return;
            int toSelect = psList->GetSelectedIndex();

            if( toSelect == song->segments.size() - 1 )
            {
                toSelect = song->segments.size() - 2;
            }

            if( song->RemoveSegment( toSelect ) )
            {
                SynchronizeWidgetsWithSong( toSelect );
                Player::GetInstance()->Synchronize();
            }
        }
        else    // loop edit mode
        {
            int sel = psList->GetSelectedIndex();
            if( sel !=  -1)
            {
                song->RemoveLoop( sel );
                if( song->loops.size() > 0 )
                {
                    if( sel == song->loops.size() )
                    {
                        sel = song->loops.size() - 1;
                    }
                    SynchronizeWidgetsWithSong( sel );
                }
                else
                {
                    SynchronizeWidgetsWithSong( -1 );
                }
            }
        }
    } else if( b == psInsertButton )
    {
        if( song->InsertSegment( psList->GetSelectedIndex() ) )
        {
            SynchronizeWidgetsWithSong();
            Player::GetInstance()->Synchronize();
        }
    } else if( b == psClearButton )
    {
        song->ClearSegments();
        SynchronizeWidgetsWithSong( 0 );
        Player::GetInstance()->Synchronize();
    } else if( b == psSetStartButton )
    {
        if( currentMode == PSEditMode )
        {
            int newIndex = song->SetSegmentStart( psList->GetSelectedIndex(), Player::GetInstance()->GetTime() );
            if( newIndex != -1 )
            {
                SynchronizeWidgetsWithSong( newIndex );
                Player::GetInstance()->Synchronize();
            }
        }
        else    // loop edit mode
        {
            song->SetLoopStart( psList->GetSelectedIndex(), Player::GetInstance()->GetTime() );
            SynchronizeWidgetsWithSong( psList->GetSelectedIndex() );
        }
    } else if( b == psSetEndButton )
    {
        int sel = psList->GetSelectedIndex();
        if( currentMode == PSEditMode )
        {
            if( song->SetSegmentEnd( sel, Player::GetInstance()->GetTime() ) )
            {
                SynchronizeWidgetsWithSong( sel );
                Player::GetInstance()->Synchronize();
            }
        }
        else    // loop edit mode
        {
            song->SetLoopEnd( sel, Player::GetInstance()->GetTime() );
            SynchronizeWidgetsWithSong( sel );
        }
    }
    else if( b == psSaveButton )
    {
        if( song->mpsFileName == "unknown" )
        {
            cProxy->saveSong = true;
            cProxy->prevFeatureId = ControllerProxy::EDIT_SONG_FEATURE;
            cProxy->UnloadRequest(ControllerProxy::SAVE_FILE_DIALOG_FEATURE);
        }
        else
        {
            CoreIO::SaveSong( song->mpsFileName, song );
        }
    }
}

// slots implementation
void EditSongFeature::TextChanged()
{
    if( tmpTime.elapsed() > 100 )
    {
        if( !ignoreTextChange )
        {
            int sel = psList->GetSelectedIndex();
            if( sel != -1 )
            {
                if( currentMode == PSEditMode )
                {
                    song->segments[ sel ].name = psNameBox->GetText();
                    psList->SetItemAt( sel, psNameBox->GetText(), false );
                }
                else
                {
                    song->loops[ sel ].name = psNameBox->GetText();
                    psList->SetItemAt( sel, psNameBox->GetText(), false );
                }
            }
        }
        else
        {
            ignoreTextChange = false;
        }
        tmpTime.restart();
    }

}

// initialization functions
void EditSongFeature::BuildWidgets()
{
    // menu widgets
    generalInfoLabel = new ClickableLabel(this, engine, "General info", 0, 150, 300, 20, Label::Right);
    psEditLabel = new ClickableLabel(this, engine, "Practice segment editing", 0, 190, 300, 20, Label::Right);
    loopEditLabel = new ClickableLabel(this, engine, "Loops editing", 0, 230, 300, 20, Label::Right);

    generalInfoLabel->underlined = false;
    psEditLabel->underlined = false;
    loopEditLabel->underlined = false;

    generalInfoLabel->AddClickableListener(this);
    psEditLabel->AddClickableListener(this);
    loopEditLabel->AddClickableListener(this);

    AddChild(generalInfoLabel);
    AddChild(psEditLabel);
    AddChild(loopEditLabel);

    // general info widgets
    QString infoFixedLabelText[7] = {"MPS file name:", "Active practice segments:", "Loops number:",
                                     "Song name:", "Type:", "Length:", "DefaultPitch:"};
    for(int i=0; i<7; i++)
    {
        infoFixedLabel[i] = new Label(this, engine, infoFixedLabelText[i], -1000, 50 + i*40, 300, 20);
        infoFixedLabel[i]->SetFont(QFont("Palatino", 12, QFont::Bold));
        infoLabel[i] = new Label(this, engine, "empty", -2000, infoFixedLabel[i]->GetY(), 600, 20);
        AddChild(infoLabel[i]);
        AddChild(infoFixedLabel[i]);
    }
    saveLabel = new ClickableLabel(this, engine, "Save Now", -1000, infoFixedLabel[0]->GetY(), 150, 20);
    saveLabel->AddClickableListener(this);
    AddChild(saveLabel);

    editPsLabel = new ClickableLabel(this, engine, "Edit", -1000, infoFixedLabel[1]->GetY(), 150, 20);
    editPsLabel->AddClickableListener(this);
    AddChild(editPsLabel);

    editLoopLabel = new ClickableLabel( this, engine, "Edit", -1000, infoFixedLabel[2]->GetY(), 150, 20 );
    editLoopLabel->AddClickableListener( this );
    AddChild( editLoopLabel );

    pitchSlider = new Slider( this, engine, -1000, infoFixedLabel[6]->GetY(), 300, 28 );
    pitchSlider->AddSliderListener( this );
    AddChild( pitchSlider );

    // ps editor widgets
    psList = new List( this, engine, -1000, 40, w/3, h - 120, 20 );
    psList->AddListListener( this );
    AddChild( psList );

    QString psFixedLabelText[6] = { "Name:", "Start:", "End:", "Length:", "Speed:", "Pitch:" };
    for( int i=0; i<6; i++ )
    {
        psFixedLabel[i] = new Label( this, engine, psFixedLabelText[i], -1000, 40 + 40 * i, 70, 20, Label::Right);
        psFixedLabel[i]->SetFont( QFont( "Palatino", 12, QFont::Bold ) );
        AddChild( psFixedLabel[i] );
    }

    psNameBox = new TextBox( this, engine, -1000, 35, 300, 30, mw );
    QObject::connect( psNameBox->GetTextBox(), SIGNAL( textChanged() ), this, SLOT( TextChanged() ) );
    AddChild( psNameBox );

    psStartLabel = new Label( this, engine, "", -1000, 80, 100, 20 );
    AddChild( psStartLabel );

    psEndLabel = new Label( this, engine, "", -1000, 120, 100, 20 );
    AddChild( psEndLabel );

    psLengthLabel = new Label( this, engine, "", -1000, 160, 100, 20 );
    AddChild( psLengthLabel );

    psSpeedLabel = new Label( this, engine, "", -1000, 200, 100, 20 );
    AddChild( psSpeedLabel );

    psSpeedSlider = new Slider( this, engine, -1000, 196, 300, 28 );
    psSpeedSlider->AddSliderListener( this );
    AddChild( psSpeedSlider );

    psPitchLabel = new Label( this, engine, "", -1000, 240, 150, 20 );
    AddChild( psPitchLabel );

    psPitchSlider = new Slider( this, engine, -1000, 236, 300, 28 );
    psPitchSlider->AddSliderListener( this );
    AddChild( psPitchSlider );

    psTitleLabel = new Label( this, engine, "", -1000, 0, w - 150, 20 );
    psTitleLabel->penBlue = 255;
    AddChild( psTitleLabel );

    psPlayerSlider = new PSSlider( this, engine, -1000, 340, 500, 48 );
    psPlayerSlider->AddSliderListener( this );
    AddChild( psPlayerSlider );

    psTimeLabel = new Label( this, engine, "", -1000, 280, 200, 50, Label::Right );
    psTimeLabel->SetFont( QFont( "Palatino", 22 ) );
    AddChild( psTimeLabel );

    int bWidth = psList->GetWidth()/2 - 10;
    int bHeight = ( h - ( psList->GetY() + psList->GetHeight() ) - 20 ) / 2;

    psAddButton = new Button( this, engine, "Add", -1000, psList->GetY() + psList->GetHeight() + 10, bWidth, bHeight );
    psAddButton->AddButtonListener( this );
    AddChild( psAddButton );

    psRemoveButton = new Button( this, engine, "Remove", -1000, psAddButton->GetY(), bWidth, bHeight );
    psRemoveButton->AddButtonListener( this );
    AddChild( psRemoveButton );

    psInsertButton = new Button( this, engine, "Insert", -1000, psAddButton->GetY() + bHeight + 10, bWidth, bHeight );
    psInsertButton->AddButtonListener( this );
    AddChild( psInsertButton );

    psClearButton = new Button( this, engine, "Clear", -1000, psInsertButton->GetY(), bWidth, bHeight );
    psClearButton->AddButtonListener( this );
    AddChild( psClearButton );

    psSetStartButton = new Button( this, engine, "Set Now!", -1000, psStartLabel->GetY() - 5, 100, 30 );
    psSetStartButton->AddButtonListener( this );
    AddChild( psSetStartButton );

    psSetEndButton = new Button( this, engine, "Set Now!", -1000, psEndLabel->GetY() - 5, 100, 30 );
    psSetEndButton->AddButtonListener( this );
    AddChild( psSetEndButton );

    psControls = new PlayerControls( this, engine, -1000,
                                     psPlayerSlider->GetY() + psPlayerSlider->GetHeight() + 40, 500, 60 );
    psControls->AddPlayerControlsListener( this );
    AddChild( psControls );

    psInPlayFixedLabel = new Label( this, engine, "In Play:", -1000, 0, psFixedLabel[0]->GetWidth(), 20, Label::Right );
    psInPlayFixedLabel->SetY( psTimeLabel->GetY() + psTimeLabel->GetHeight() - psInPlayFixedLabel->GetHeight() );
    psInPlayFixedLabel->SetFont( QFont( "Palatino", 12, QFont::Bold ) );
    AddChild( psInPlayFixedLabel );

    psInPlayLabel = new Label( this, engine, "", -1000, psInPlayFixedLabel->GetY(), 300, 20 );
    AddChild( psInPlayLabel );

    psSaveButton = new Button( this, engine, "Save Changes", -1000,
                               psControls->GetY() + psControls->GetHeight() + 35, 150, 30 );
    psSaveButton->AddButtonListener( this );
    AddChild( psSaveButton );

    // loop editor widget
    lpActiveFixedLabel = new Label( this, engine, "Active:", -1000, 200,
                                    psFixedLabel[0]->GetWidth(), 20, Label::Right );
    lpActiveFixedLabel->SetFont( QFont( "Palatino", 12, QFont::Bold ) );
    AddChild( lpActiveFixedLabel );

    QImage* onImg = ImagesManager::GetInstance()->GetImage( ImagesManager::ON );
    QImage* offImg = ImagesManager::GetInstance()->GetImage( ImagesManager::OFF );

    onPixmap = new QPixmap( 20, 20 );
    onPixmap->fill( QColor( 0, 0, 0, 0 ) );
    QPainter onPixmapPainter( onPixmap );
    onPixmapPainter.drawImage( onPixmap->rect(), *onImg, onImg->rect() );
    onPixmapPainter.end();

    offPixmap = new QPixmap( 20, 20 );
    offPixmap->fill( QColor( 0, 0, 0, 0 ) );
    QPainter offPixmapPainter( offPixmap );
    offPixmapPainter.drawImage( offPixmap->rect(), *offImg, offImg->rect() );
    offPixmapPainter.end();

    lpActiveRect = QRect( x + psList->GetWidth() + 20 + psFixedLabel[0]->GetWidth(),
                          y + 200, 20, 20 );

    lpActiveLabel = new Label( this, engine, "", -1000, 200, 350, 20 );
    AddChild( lpActiveLabel );
}

void EditSongFeature::BuildAnimations()
{
    generalInfoX = 0;
    psEditLabelX = 0;
    loopEditLabelX = 0;
    headerLineLength = 0;

    // show menu animation
    animeGraph.AddSimpleProperty("generalInfoX", generalInfoX);
    animeGraph.AddSimpleProperty("psEditLabelX", psEditLabelX);
    animeGraph.AddSimpleProperty("loopEditLabelX", loopEditLabelX);

    generalInfoX_id = animeGraph.AddInterpolator(showMenuAnime.GetTime(), 0, 400, "generalInfoX", 0, 300);
    psEditLabelX_id = animeGraph.AddInterpolator(showMenuAnime.GetTime(), 0, 400, "psEditLabelX", 0, 300);
    loopEditLabelX_id = animeGraph.AddInterpolator(showMenuAnime.GetTime(), 0, 400, "loopEditLabelX", 0, 300);

    engine->AddAnimation(&showMenuAnime);
    showMenuAnime.AddObserver(this);

    // general info show
    animeGraph.AddSimpleProperty("headerLineLength", headerLineLength);
    generalInfoY_id = animeGraph.AddInterpolator(editMenuShowAnime.GetTime(), 0, 500,
                                                 Interpolable::GetProperty<int>(generalInfoLabel, "y"), 0, 150, &om_ni);
    psEditLabelY_id = animeGraph.AddInterpolator(editMenuShowAnime.GetTime(), 0, 500,
                                                 Interpolable::GetProperty<int>(psEditLabel, "y"), 0, 190, &om_ni);
    loopEditLabelY_id = animeGraph.AddInterpolator(editMenuShowAnime.GetTime(), 0, 500,
                                                   Interpolable::GetProperty<int>(loopEditLabel, "y"), 0, 230, &om_ni);
    generalInfoTx_id = animeGraph.AddInterpolator(editMenuShowAnime.GetTime(), 0, 500,
                                                  Interpolable::GetProperty<int>(generalInfoLabel, "x"), 0, - (300 - generalInfoLabel->GetTextWidth()));
    psEditLabelTx_id = animeGraph.AddInterpolator(editMenuShowAnime.GetTime(), 0, 500,
                                                  Interpolable::GetProperty<int>(psEditLabel, "x"), 0, - (300 - psEditLabel->GetTextWidth()));
    loopEditLabelTx_id = animeGraph.AddInterpolator(editMenuShowAnime.GetTime(), 0, 500,
                                                    Interpolable::GetProperty<int>(loopEditLabel, "x"), 0, - (300 - loopEditLabel->GetTextWidth()));
    animeGraph.AddInterpolator(editMenuShowAnime.GetTime(), 500, 800, "headerLineLength", 0, w - 300);
    animeGraph.AddInterpolator(editMenuShowAnime.GetTime(), 0, 500, "viewMenuLabelAlpha", 0, 255);

    engine->AddAnimation(&editMenuShowAnime);
    editMenuShowAnime.AddObserver(this);
}

// animations support functions
void EditSongFeature::UnderlineText(QPainter& painter, ClickableLabel* l, int* px)
{
    int lineY = y + l->GetY() + l->GetHeight();
    painter.drawLine(x, lineY, x + *px, lineY);
}

// editors load and unload functions
void EditSongFeature::LoadGeneralInfo()
{
    QString lengthString = CoreUtils::GetHHMMSS( song->length, false );
    QString pitchString = CoreUtils::GetPitchString( song->defaultPitch );

    QString infoLabelText[7] = { song->mpsFileName, QString::number(song->segments.size()),
                                 QString::number(song->loops.size()),
                                 song->songName, song->format, lengthString,
                                 pitchString};

    for(int i=0; i<7; i++)
    {
        infoFixedLabel[i]->SetX(0);
        infoLabel[i]->SetText(infoLabelText[i]);
        infoLabel[i]->SetX(infoFixedLabel[i]->GetTextWidth() + 10);
    }

    if(song->mpsFileName == "unknown")
    {
        saveLabel->SetX(infoLabel[0]->GetX() + infoLabel[0]->GetTextWidth() + 50);
    }
    editPsLabel->SetX(infoLabel[1]->GetX() + 60);
    editLoopLabel->SetX( editPsLabel->GetX() );
    pitchSlider->SetX( infoLabel[6]->GetX() );
    pitchSlider->SetY( infoLabel[6]->GetY() + 35 );
    pitchSlider->SetPosition( ( 12 + song->defaultPitch ) / 24.0 );

    currentMode = GeneralInfo;
    showingGeneralInfo = false;
}

void EditSongFeature::LoadPSEditor()
{
    // load ps editor..
    currentMode = PSEditMode;
    showingPsEditor = false;
    playing = false;
    Player::GetInstance()->loopsTracking = false;
    Player::GetInstance()->SetPlaybackMode( true );
    psTitleLabel->SetText( " - " + song->songName );

    psList->SetX( 0 );
    for( int i=0; i<6; i++ )
    {
        psFixedLabel[i]->SetX( psList->GetWidth() + 10 );
    }
    psPlayerSlider->SetPosition( 0.0 );

    psNameBox->SetX( psFixedLabel[0]->GetX() + psFixedLabel[0]->GetWidth() + 10 );
    psStartLabel->SetX( psNameBox->GetX() );
    psEndLabel->SetX( psNameBox->GetX() );
    psLengthLabel->SetX( psNameBox->GetX() );
    psSpeedLabel->SetX( psNameBox->GetX() );
    psPitchLabel->SetX( psNameBox->GetX() );
    psPitchSlider->SetX( psNameBox->GetX() + 120 );
    psSpeedSlider->SetX( psPitchSlider->GetX() );
    psTitleLabel->SetX( psEditLabel->GetTextWidth() );
    psPlayerSlider->SetX( psFixedLabel[0]->GetX() );
    psTimeLabel->SetX( psPlayerSlider->GetX() + psPlayerSlider->GetWidth() - psTimeLabel->GetWidth() - 5 );
    psAddButton->SetX( 0 );
    psRemoveButton->SetX( psList->GetWidth()/2 + 10 );
    psInsertButton->SetX( 0 );
    psClearButton->SetX( psRemoveButton->GetX() );
    psSetStartButton->SetX( psSpeedSlider->GetX() );
    psSetEndButton->SetX( psSetStartButton->GetX() );
    psControls->SetX( psPlayerSlider->GetX() );
    psInPlayFixedLabel->SetX( psFixedLabel[0]->GetX() );
    psInPlayLabel->SetX( psNameBox->GetX() );
    psSaveButton->SetX( psControls->GetX() + psControls->GetWidth() - psSaveButton->GetWidth() );

    Player::GetInstance()->SetVolume( psControls->volPos );
    psControls->playing = false;
    updateSlider = true;

    SynchronizeWidgetsWithSong( 0 );
}

void EditSongFeature::LoadLoopEditor()
{
    // load loop editor..
    currentMode = LoopEditMode;
    Player::GetInstance()->SetPlaybackMode( false );
    showingLoopEditor = false;

    psTitleLabel->SetText( " - " + song->songName );
    psList->SetX( 0 );
    for( int i=0; i<4; i++ )
    {
        psFixedLabel[i]->SetX( psList->GetWidth() + 10 );
    }
    psNameBox->SetX( psFixedLabel[0]->GetX() + psFixedLabel[0]->GetWidth() + 10 );
    psStartLabel->SetX( psNameBox->GetX() );
    psEndLabel->SetX( psNameBox->GetX() );
    psLengthLabel->SetX( psNameBox->GetX() );
    psTitleLabel->SetX( loopEditLabel->GetTextWidth() );
    psAddButton->SetX( 0 );
    psRemoveButton->SetX( psList->GetWidth()/2 + 10 );
    psTimeLabel->SetX( psPlayerSlider->GetX() + psPlayerSlider->GetWidth() - psTimeLabel->GetWidth() - 5 );
    psPlayerSlider->SetX( psFixedLabel[0]->GetX() );
    psControls->SetX( psPlayerSlider->GetX() );
    psSetStartButton->SetX( psSpeedSlider->GetX() );
    psSetEndButton->SetX( psSetStartButton->GetX() );
    psTimeLabel->SetX( psPlayerSlider->GetX() + psPlayerSlider->GetWidth() - psTimeLabel->GetWidth() - 5 );
    lpActiveFixedLabel->SetX( psFixedLabel[0]->GetX() );
    lpActiveLabel->SetText( "" );
    lpActiveLabel->SetX( psNameBox->GetX() + 30 );
    psSaveButton->SetX( psControls->GetX() + psControls->GetWidth() - psSaveButton->GetWidth() );

    psPlayerSlider->SetPosition( 0.0 );
    psList->SetStringList( QStringList() );
    QList< double > l;
    psPlayerSlider->SetHighlightPoints( l );
    psPlayerSlider->Highlight( 0, 0 );
    Player::GetInstance()->SetVolume( psControls->volPos );
    psControls->playing = false;
    updateSlider = true;

    if( song->loops.size() > 0 )
    {
        SynchronizeWidgetsWithSong( 0 );
    }
    else
    {
        SynchronizeWidgetsWithSong( -1 );
    }
}

void EditSongFeature::UnloadGeneralInfo()
{
    for(int i=0; i<7; i++)
    {
        infoFixedLabel[i]->SetX(-1000);
        infoLabel[i]->SetX(-2000);
    }
    saveLabel->SetX( -1000 );
    editPsLabel->SetX( -1500 );
    editLoopLabel->SetX( -1500 );
    pitchSlider->SetX( - 1500 );

    UnloadDone();
}

void EditSongFeature::UnloadPSEditor()
{
    Player::GetInstance()->loopsTracking = true;
    psList->SetX( -1000 );

    for( int i=0; i<6; i++ )
    {
        psFixedLabel[i]->SetX( -1000 );
    }

    psNameBox->SetX( -1000 );
    psStartLabel->SetX( -1000 );
    psEndLabel->SetX( -1000 );
    psLengthLabel->SetX( -1000 );
    psSpeedLabel->SetX( -1000 );
    psSpeedSlider->SetX( -1000 );
    psPitchLabel->SetX( -1000 );
    psPitchSlider->SetX( -1000 );
    psTitleLabel->SetX( -1000 );
    psPlayerSlider->SetX( -1000 );
    psTimeLabel->SetX( -1000 );
    psAddButton->SetX( -1000 );
    psRemoveButton->SetX( -1000 );
    psInsertButton->SetX( -1000 );
    psClearButton->SetX( -1000 );
    psSetStartButton->SetX( -1000 );
    psSetEndButton->SetX( -1000 );
    psControls->SetX( -1000 );
    psInPlayFixedLabel->SetX( -1000 );
    psInPlayLabel->SetX( -1000 );
    psSaveButton->SetX( -1000 );

    Player::GetInstance()->Stop();
    UnloadDone();
}

void EditSongFeature::UnloadLoopEditor()
{
    psList->SetX( -1000 );

    for( int i=0; i<4; i++ )
    {
        psFixedLabel[i]->SetX( -1000 );
    }
    psNameBox->SetX( -1000 );
    psStartLabel->SetX( -1000 );
    psEndLabel->SetX( -1000 );
    psLengthLabel->SetX( -1000 );
    psTitleLabel->SetX( -1000 );
    psAddButton->SetX( -1000 );
    psRemoveButton->SetX( -1000 );
    psTimeLabel->SetX( -1000 );
    psPlayerSlider->SetX( -1000 );
    psControls->SetX( -1000 );
    psSetStartButton->SetX( -1000 );
    psSetEndButton->SetX( -1000 );
    psTimeLabel->SetX( -1000 );
    lpActiveFixedLabel->SetX( -1000 );
    lpActiveLabel->SetX( -1000 );
    psSaveButton->SetX( -1000 );

    Player::GetInstance()->Stop();
    Player::GetInstance()->SetPlaybackMode( true );
    UnloadDone();
}

void EditSongFeature::UnloadDone()
{
    editMenuShowAnime.Reverse(true);
    editMenuShowAnime.Play();
}

void EditSongFeature::SynchronizeWidgetsWithSong( int elementToSelect )
{
    int toSelect = elementToSelect;
    if( toSelect == -1 && currentMode == PSEditMode ) // <-- ugly but necessary
    {
        toSelect = psList->GetSelectedIndex();
    }

    // fills the list
    QStringList items;
    if( currentMode == PSEditMode )
    {
        for ( int i=0; i<song->segments.size(); i++ )
        {
            items.append( song->segments[i].name );
        }
    }
    else
    {
        for ( int i=0; i<song->loops.size(); i++ )
        {
            items.append( song->loops[i].name );
        }
    }
    psList->SetStringList( items );

    if( currentMode == PSEditMode )
    {
        // set the highlights
        QList< double > highlights;
        for( int i=0; i<song->segments.size(); i++ )
        {
            if( song->segments[i].end < song->length )
            {
                highlights << ( song->segments[i].end / (double)song->length );
            }
        }
        psPlayerSlider->SetHighlightPoints( highlights );
    }

    if( toSelect != -1 )
    {
        psList->SetItemSelected( toSelect, false );
        OnSelect( psList, toSelect );
    }
    else
    {
        if( currentMode == LoopEditMode )
        {
            psPlayerSlider->Highlight( 0, 0 );
            OnSelect( psList, -1 );
        }
    }
}















